/**
 饿/**
 * @file main.cpp
 *
 * Copyright (C) 2025. Huawei Technologies Co., Ltd. All rights reserved.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 */
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <cmath>
#include <cstdlib>

#include <cstdint>
#include <iostream>

#include "acl/acl.h"
#include "common.h"
#include "op_runner.h"

bool g_isDevice = false;
int deviceId = 0;

OperatorDesc CreateOpDesc()
{
    // 读取start、end、step的第一个元素来计算输出长度
    const size_t elemSize = sizeof(float);
    
    // 读取start的第一个元素
    float *startBuffer = reinterpret_cast<float*>(malloc(elemSize));
    float *endBuffer   = reinterpret_cast<float*>(malloc(elemSize));
    float *stepBuffer  = reinterpret_cast<float*>(malloc(elemSize));
    if (startBuffer == nullptr || endBuffer == nullptr || stepBuffer == nullptr) {
        ERROR_LOG("Malloc input buffers failed, start=%p end=%p step=%p",
                  (void*)startBuffer, (void*)endBuffer, (void*)stepBuffer);
        free(startBuffer);
        free(endBuffer);
        free(stepBuffer);
        return OperatorDesc();
    }

    size_t fileSize = 0;
    if (!ReadFile("../input/starts.bin", fileSize, startBuffer, elemSize)) {
        ERROR_LOG("Read starts.bin failed");
        free(startBuffer);
        free(endBuffer);
        free(stepBuffer);
        return OperatorDesc();
    }
    if (!ReadFile("../input/ends.bin", fileSize, endBuffer, elemSize)) {
        ERROR_LOG("Read ends.bin failed");
        free(startBuffer);
        free(endBuffer);
        free(stepBuffer);
        return OperatorDesc();
    }
    if (!ReadFile("../input/ns.bin", fileSize, stepBuffer, elemSize)) {
        ERROR_LOG("Read ns.bin failed");
        free(startBuffer);
        free(endBuffer);
        free(stepBuffer);
        return OperatorDesc();
    }
    
    // 计算输出长度：正确的range计算逻辑
    float start = startBuffer[0];
    float end   = endBuffer[0];
    float step  = stepBuffer[0];
    
    // 防止除零错误
    if (step == 0.0f) {
        ERROR_LOG("Step cannot be zero");
        free(startBuffer);
        free(endBuffer);
        free(stepBuffer);
        return OperatorDesc();
    }

    // 正确的 range 输出长度计算
    // 规则：类似 Python range，方向必须一致，否则为空序列
    int64_t sequenceLength = 0;
    double dStart = static_cast<double>(start);
    double dEnd   = static_cast<double>(end);
    double dStep  = static_cast<double>(step);

    if ((step > 0.0f && start < end) || (step < 0.0f && start > end)) {
        // 方向正确：从 start 开始按 step 走，不超过 end，包含起点
        double diff = dEnd - dStart;
        double steps = diff / dStep;  // 可能为负，但条件保证号召意义正确
        // 对正向/反向统一用 floor 取“完整步数”，再 +1 表示包含起点
        sequenceLength = static_cast<int64_t>(std::floor(steps)) + 1;
        if (sequenceLength < 0) {
            // 理论上不该出现，为安全起见防御一下
            sequenceLength = 0;
        }
    } else {
        // 方向不匹配，返回空序列
        sequenceLength = 0;
    }

    // 调试信息：打印计算过程
    std::cout << "DEBUG: start=" << start << ", end=" << end << ", step=" << step << std::endl;
    std::cout << "DEBUG: sequenceLength=" << sequenceLength << std::endl;
    std::cout << "DEBUG: (end-start)/step=" << ((end - start) / step) << std::endl;
    
    free(startBuffer);
    free(endBuffer);
    free(stepBuffer);
    
    startBuffer = nullptr;
    endBuffer = nullptr;
    stepBuffer = nullptr;

    const std::vector<int64_t>elemShape{1};  // 单元素形状
    const std::vector<int64_t>shape{sequenceLength};

    // 修复数据类型不匹配：所有输入都是float32，输出也是float32
    aclDataType inputType = ACL_FLOAT;
    aclDataType outputType = ACL_FLOAT;
    const aclFormat format = ACL_FORMAT_ND;

    OperatorDesc opDesc;
    // 有几个输入就AddInputTensorDesc几次
    // 所有三个输入都是float32类型，与生成的数据一致
    opDesc.AddInputTensorDesc(inputType, elemShape.size(), elemShape.data(), format);
    opDesc.AddInputTensorDesc(inputType, elemShape.size(), elemShape.data(), format);
    opDesc.AddInputTensorDesc(inputType, elemShape.size(), elemShape.data(), format);
    opDesc.AddOutputTensorDesc(outputType, shape.size(), shape.data(), format);
    // 修正格式化字符串与参数类型不匹配的问题，避免未定义行为导致的潜在段错误
    INFO_LOG("Create OpDesc success: start=%.2f, end=%.2f, step=%.2f, output_length=%d",
             start, end, step, static_cast<int>(sequenceLength));
    return opDesc;
}

bool SetInputData(OpRunner &runner)
{
    size_t fileSize = 0;
    // 确认输入个数
    ReadFile("../input/starts.bin", fileSize, runner.GetInputBuffer<void>(0), runner.GetInputSize(0));
    ReadFile("../input/ends.bin", fileSize, runner.GetInputBuffer<void>(1), runner.GetInputSize(1));
    ReadFile("../input/ns.bin", fileSize, runner.GetInputBuffer<void>(2), runner.GetInputSize(2));
    INFO_LOG("Set input success");
    return true;
}

bool ProcessOutputData(OpRunner &runner)
{
    // GetOutputBuffer 返回的是 const 指针，我们在这里只做写文件，不修改数据，因此保持 const
    const void *outBuf   = runner.GetOutputBuffer<void>(0);
    size_t      outSize  = runner.GetOutputSize(0);

    // 当输出为空或 buffer 为 nullptr 时，安全地跳过写文件，避免未定义行为
    if (outBuf == nullptr || outSize == 0) {
        WARN_LOG("ProcessOutputData: output buffer is null or size is 0 (buf=%p, size=%zu)",
                 outBuf, outSize);
        return false;
    }

    if (!WriteFile("../output/output_z.bin", outBuf, outSize)) {
        ERROR_LOG("ProcessOutputData: WriteFile failed (buf=%p, size=%zu)", outBuf, outSize);
        return false;
    }

    INFO_LOG("Write output success");
    return true;
}

void DestroyResource()
{
    bool flag = false;
    if (aclrtResetDevice(deviceId) != ACL_SUCCESS) {
        ERROR_LOG("Reset device %d failed", deviceId);
        flag = true;
    }
    INFO_LOG("Reset Device success");
    if (aclFinalize() != ACL_SUCCESS) {
        ERROR_LOG("Finalize acl failed");
        flag = true;
    }
    if (flag) {
        ERROR_LOG("Destroy resource failed");
    } else {
        INFO_LOG("Destroy resource success");
    }
}

bool InitResource()
{
    std::string output = "../output";
    if (access(output.c_str(), 0) == -1) {
        int ret = mkdir(output.c_str(), 0700);
        if (ret == 0) {
            INFO_LOG("Make output directory successfully");
        } else {
            ERROR_LOG("Make output directory fail");
            return false;
        }
    }

    // acl.json is dump or profiling config file
    if (aclInit("../scripts/acl.json") != ACL_SUCCESS) {
        ERROR_LOG("acl init failed");
        return false;
    }

    if (aclrtSetDevice(deviceId) != ACL_SUCCESS) {
        ERROR_LOG("Set device failed. deviceId is %d", deviceId);
        (void)aclFinalize();
        return false;
    }
    INFO_LOG("Set device[%d] success", deviceId);

    // runMode is ACL_HOST which represents app is running in host
    // runMode is ACL_DEVICE which represents app is running in device
    aclrtRunMode runMode;
    if (aclrtGetRunMode(&runMode) != ACL_SUCCESS) {
        ERROR_LOG("Get run mode failed");
        DestroyResource();
        return false;
    }
    g_isDevice = (runMode == ACL_DEVICE);
    INFO_LOG("Get RunMode[%d] success", runMode);

    return true;
}

bool RunOp()
{
    // create op desc
    OperatorDesc opDesc = CreateOpDesc();

    // create Runner
    OpRunner opRunner(&opDesc);
    if (!opRunner.Init()) {
        ERROR_LOG("Init OpRunner failed");
        return false;
    }

    // Load inputs
    if (!SetInputData(opRunner)) {
        ERROR_LOG("Set input data failed");
        return false;
    }

    // Run op
    if (!opRunner.RunOp()) {
        ERROR_LOG("Run op failed");
        return false;
    }

    // process output data
    if (!ProcessOutputData(opRunner)) {
        ERROR_LOG("Process output data failed");
        return false;
    }

    INFO_LOG("Run op success");
    return true;
}

int main(int argc, char **argv)
{
    std::cout << "DEBUG: Starting main function" << std::endl;
    
    if (!InitResource()) {
        ERROR_LOG("Init resource failed");
        return FAILED;
    }
    INFO_LOG("Init resource success");

    if (!RunOp()) {
        ERROR_LOG("Execute main func failed");
        INFO_LOG("ENTER DESTROY RESOURCE IN MAIN (after RunOp failed)");
        DestroyResource();
        INFO_LOG("LEAVE DESTROY RESOURCE IN MAIN (after RunOp failed)");
        return FAILED;
    }

    INFO_LOG("ENTER DESTROY RESOURCE IN MAIN (normal path)");
    DestroyResource();
    INFO_LOG("LEAVE DESTROY RESOURCE IN MAIN (normal path)");

    INFO_LOG("Before returning from main()");
    return SUCCESS;
}
